"""--------------------------------------------------------------------------------------------------
Filename:   NewPlot.py
Author:     Chris Chester
Date:       08 02 2013
Description:
    Main plotting module for biofeedback application.
    Contains the GUI layer and plotting functions.
    
    inputs: 
        app = the main app object
        title = what to be displayed as the title of the graph 
--------------------------------------------------------------------------------------------------"""
from pyqtgraph.Qt import QtGui, QtCore
import sys
import numpy as np
import pyqtgraph as pg
import frameIcons
from plotpanel import *
from targetfactory import TargetFactory
import database
from PIL import Image
from config import Modes
from decimal import Decimal
from pyqtgraph.dockarea import DockArea,Dock
import gc
import time
import os
import numpy as np

#===============================================================================
# CLASS: Console
#    Launches and maintains the plotting window for emg data
#===============================================================================
class NewPlot():
    def __init__(self, app, title="New Style plot frame"):
        self.app = app
        self.qapp = QtGui.QApplication([])
        self.mw = QtGui.QMainWindow()
        self.area = DockArea()
        
        #Setting up the mainwindow
        self.mw.setCentralWidget(self.area)
        self.mw.resize(1200,800)
        self.mw.setWindowTitle('Simplex sEMG Session')

        # Timer interrupt for calling the display
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.update_display)
        self.timer.start(self.app.cfg.sample_rate)
        
        # The maximum X value on the x axis
        self.xMax = self.app.cfg.trial_len
        #self.app.cfg.y_axis_max = self.app.cfg.y_axis_max_init --removed 05/02
        
        self.__setup_plot()
        self.__do_layout()

    def __do_layout(self):
        '''
        Sets-up the layout for the display window
        '''
        self.area.addDock(self.dPlot, 'top')      ## place plot at Top
        self.area.addDock(self.dCntl, 'bottom')     ## place control panel at Bottom
        self.area.addDock(self.dConn, 'right',self.dCntl)     ## place control panel at Bottom
        self.area.addDock(self.dCfg,'right',self.dConn)
        self.area.addDock(self.dOpt, 'above',self.dCfg)     ## place control panel at Bottom
        
        self.area.addDock(self.dSts, 'right',self.dOpt)     ## place control panel at Bottom
        
        mode = self.app.sess_status.mode
        if mode == Modes.SklTrl or mode == Modes.SklCal:
            self.area.addDock(self.dRng, 'right',self.dSts)     ## place control panel at Bottom
        else:
            self.area.addDock(self.dBse, 'right',self.dSts)     ## place control panel at Bottom
            
        self.wGraph.setXRange(0,self.xMax)
        self.wGraph.setYRange(0,self.app.cfg.y_axis_max)
        
        self.dPlot.hideTitleBar()
        
        self.dPlot.addWidget(self.wGraph)
        
        self.mw.show()
        
    def __setup_plot(self):
        '''
        Sets-up the widgets for the display window
        '''
        pg.setConfigOption('background', self.app.cfg.plot_back_color) # Background
        pg.setConfigOption('foreground', 'k') # Black Axes
        
          
        self.dPlot = Dock("Graph", size=(self.app.cfg.plot_y_pixel,self.app.cfg.plot_x_pixel))
        self.dCntl = PlotControl(name = "Controls", size=(1,1),app = self.app)
        self.dConn = PlotConnect(name = "Controls", size=(1,1),app = self.app)
        self.dOpt = PlotOptions(name = "Option", size=(1,1),app = self.app)
        self.dCfg = PlotConfig(name = "Config", size=(1,1),app = self.app)
        self.dSts = PlotStatus(name = "Trial Status", size=(1,1),app =self.app)
        self.dRng = PlotRange(name = "Target Range Limits", size=(1,1),app =self.app)
        self.dBse = PlotBase(name = "Target Baseline", size=(1,1),app =self.app)
        
        
        self.wGraph = pg.PlotWidget()    
        # Setting up the features to be displayed in the main window
        ## add image        
       
        im = Image.open(os.path.dirname(os.path.realpath(__file__))+'/images/mouth.jpg') 
        nIm = np.array(im)
        self.img = pg.ImageItem(nIm)
        self.wGraph.addItem(self.img)    

        self.sizeRect = QtCore.QRect(0,0,0,0)
        self.img.setRect(self.sizeRect)
        self.img.setPxMode(True)
        
        
        # Plots
        self.rect = self.wGraph.plot(pen=(self.app.cfg.target_color),brush = self.app.cfg.target_color) # Green box
        self.curve = self.wGraph.plot(pen= self.app.cfg.plot_line_color) # Black Line 
        
        self.rect.setPen(color =self.app.cfg.target_color ,width = self.app.cfg.target_line_width)
        self.curve.setPen(color = self.app.cfg.plot_line_color,width = self.app.cfg.plot_line_width)        
        #Cursor Lines to be added
        self.timeLine = self.wGraph.plot(pen='r') # Red time line
        self.ampLine = self.wGraph.plot(pen='r') # Red amplitude Line 
        
        # Instantiate a target factor y to be used in skill training
        self.tfactory = TargetFactory(self.app.cfg, self.app.sess_status)
        
        self.rect_x = [0,0,0,0]
        self.rect_y = [0,0,0,0]
        
        self.x_data = np.array([])      # array of x values for the plot
        self.y_data = np.array([])      # array of y values for the plot
        
        self.plot_state= np.array([]) 
        self.emgdata = []               # used to store raw EMG data
        
        self.total_paused_time = 0
        self.time_pause = 0
        self.showCurve = True
        
        print("Start")
        
        self.start_time = time.time()
        
        self.init_plot()
    
    def set_ylimits(self,lower = 0,upper = 1):
        """
        sets the y limits of the graph
        
        ==============  =====================================
        **Arguments:**
        lower            (int) The minimum Y value
        upper            (int) The maximum Y value
        ==============  =====================================
        """
        self.wGraph.setYRange(lower,upper)
    
        
    def update_display(self):
        """
        Redisplays the sEMG data every time called
        """
        
        s_state = self.app.sess_status
        plotConfig = self.app.cfg
        
        x = self.x_data
        y = self.y_data
        
        
        # Compensates for delay if enabled 
        if self.app.cfg.use_delay and self.app.sess_status.mode == Modes.SklTrl:
            if(x.any() and y.any()): # Make Sure arrays have data
                delta = self.x_data[-1] - float(self.app.sess_status.delay_mark)
                if delta > 0:
                    self.show_plot(True)
            
        
        if self.showCurve:
                        
            if(x.any() and y.any()): # Make Sure arrays have data
                
                # Ensure the Lists are the same shape
                lenx = len(x)
                leny = len(y)
                if not lenx == leny:
                    diff = leny - lenx
                    if diff > 0:                # if y_data has more items than x_data...
                        y = y[:-diff]   # remove items from y_data to even up the arrays
                    elif diff < 0:              # if x_data has more items than y_data...
                        x = x[:-diff]   # remove items from x_data to even up the arrays
                        
                        
                self.curve.setData(x,y)
                 
                # Set y-axis limits if in calibration
                if s_state.ymax_auto:
                    if s_state.mode == Modes.StrCal or s_state.mode == Modes.SklCal:
                        # Check line and target box heights
                        if s_state.peak[1] > (self.app.cfg.ymax_auto_value * 0.8) or (self.rect_y[2] > (self.app.cfg.ymax_auto_value * 0.8)):
                            
                            if s_state.peak[1] > self.rect_y[2] :
                                self.app.cfg.ymax_auto_value = s_state.peak[1] * (1 + (plotConfig.y_margin_percent / 100.0))
                                
                            else:
                                self.app.cfg.ymax_auto_value = self.rect_y[2]* (1 + (plotConfig.y_margin_percent / 100.0)) 
                                
                    self.app.cfg.y_axis_max = self.app.cfg.ymax_auto_value
                                  
                self.wGraph.setYRange(0, self.app.cfg.y_axis_max) 
                
                
        else:
            self.curve.setData([0],[0])
        
        if(x.any() and y.any()): # Make Sure arrays have data
            self.__draw_cursors(x[-1], y[-1])
            
    def pos_to_pixel(self,xPos,yPos):
        """
        Returns the positions in pixel co-ords
        
        ==============  =====================================
        **Arguments:**
        xPos           (int) The x position in seconds wanting to
                        be converted
        yPos           (int) The y position in uV wanting to
                        be converted               
        ==============  =====================================
        """
        geo = self.area.geometry()
    
        pixHeight = geo.height()
        pixWidth = geo.width()
        padding = 1.05      # from Graphics View
        yAdjust = 1.1875# The adjustment needed to let the pic fit the box size
        xPixPos = xPos*padding/(self.xMax) * pixWidth
        yPixPos = yPos/(self.app.cfg.y_axis_max*yAdjust) * pixHeight

        
        return [xPixPos,yPixPos]
        
    def update_data(self,emglevel):
        """
        Adds the next data point to be plotted for the sEMG signal
        
        ==============  =====================================
        **Arguments:**
        emglevel        (int) The next emg value to be added to
                        the graph
        ==============  =====================================
        """
        s_state = self.app.sess_status
        plotConfig = self.app.cfg
    
        if self.app.sess_status.alive:
                
            x_index = self.get_x_value(time.time())
            
            if x_index <= plotConfig.trial_len:
                
                if emglevel > s_state.peak[1]:
                    s_state.peak = (x_index, emglevel)
    
                                           
                self.y_data = np.append(self.y_data, emglevel)
                self.x_data = np.append(self.x_data, x_index)
                
                self.plot_state =  np.append(self.plot_state, self.showCurve)
                
            else:
                # End of plot
                s_state.alive = False           # turn itself off so end_trial is only added to the queue once
                
                if s_state.mode == Modes.StrTrl or s_state.mode == Modes.SklTrl:
                    self.end_trial()   # end the trial
                else:
                    self.init_plot()    # calibration mode so just reset                
    
    def draw_grid(self,answer):
        """
        Sets the grid on the main axes to display or hide
        
        ==============  =====================================
        **Arguments:**
        answer          (bool) Whether the grid should display
        ==============  =====================================
        """
        if answer:
            self.wGraph.showGrid(x = True, y = True)
    
        else:
            self.wGraph.showGrid(x = False, y = False)
        
    def get_x_value(self,time):
        """
        Gets the time to plot on the graph
        ==============  =====================================
        **Arguments:**
        time            (int) current time from time.time 
                        command 
        ==============  =====================================
        **Output:**
        x                The time to be displayed
        ==============  =====================================
        """
        
        x = time - self.start_time - self.total_paused_time
        return x
    
    
    def __draw_target_strength(self, init=False):
        """
        Draws the target strength line based on calibration
        results
        
        ==============  =====================================
        **Arguments:**
        init            (bool) Whether or not this is the first
                        Target
        ==============  =====================================
        """
        
        self.__remove_target()
        adj = 1 - (float(self.app.cfg.str_increment) / 100)
        self.app.sess_status.peak = (0,0)
        
        if init:
            self.app.sess_status.str_target = self.app.sess_status.baseline
            self.app.ctrl.ymax_ctrl.set_value(self.app.sess_status.baseline * 1.8)    # Scale the Y-axis range
            self.app.cfg.y_axis_max = int(self.app.ctrl.ymax_ctrl.get_value())
            self.app.sess_status.result = False
        elif self.app.cfg.str_running_count:
            # Check the count and adjust target as necessary
            if self.app.sess_status.count >= self.app.cfg.str_hit_tol:      # if target has been hit consecutively, decrease size
                self.app.sess_status.str_target *= adj    # decrease size
                self.app.sess_status.count = 0
            elif self.app.sess_status.count <= -(self.app.cfg.str_miss_tol):   # if target has been missed consecutively, increase size
                self.app.sess_status.str_target /= adj    # increase size
                self.app.sess_status.count = 0
        else:
            # Check the count and adjust target as necessary
            if self.app.sess_status.target_hits >= self.app.cfg.str_hit_tol:        # if target has been hit consecutively, decrease size
                self.app.sess_status.str_target /= adj    # decrease size
                self.app.sess_status.target_hits = 0
            elif self.app.sess_status.target_misses >= self.app.cfg.str_miss_tol:   # if target has been missed consecutively, increase size
                self.app.sess_status.str_target *= adj    # decrease size
                self.app.sess_status.target_misses = 0
           
        print "update stats"
        print self.app.sess_status.target_hits       
        print self.app.sess_status.target_misses
        
        self.set_stats(self.app.sess_status.target_hits, self.app.sess_status.target_misses, self.app.sess_status.result)
            
        # Draw line on plot
        pos = [0,self.app.sess_status.str_target]
        size = [self.xMax,0]
        
        self.set_rectangle(pos = pos, size = size)
        self.app.sess_status.target_on = True
        
        # Print target details to console
        print "=== Target parameters ================================"
        print "    Amplitude:    %.2f" % self.app.sess_status.str_target
        print "======================================================"
        
        # reset the axes
        self.wGraph.setXRange(0, self.xMax)
        self.wGraph.setYRange(0, self.app.cfg.y_axis_max)

    def __draw_target_skill(self, init=False):
        """
        Draws the target skill box based on calibration
        results
        
        ==============  =====================================
        **Arguments:**
        init            (bool) Whether or not this is the first
                        Target
        ==============  =====================================
        """
        self.__remove_target()
        
        if init:
            [pos,size] = self.tfactory.recalc(0, init = True)
            self.app.sess_status.result = False

        else:
            
            if self.app.cfg.running_count:
                # Check hit count and adjust target as necessary
                if self.app.sess_status.count >= self.app.cfg.hit_tol:         # if target has been hit consecutively, decrease size
                    [pos,size] = self.tfactory.recalc(-1)
                    self.app.sess_status.count = 0
                elif self.app.sess_status.count <= -(self.app.cfg.miss_tol):   # if target has been missed consecutively, increase size
                    [pos,size] = self.tfactory.recalc(1)
                    self.app.sess_status.count = 0
                else:
                    [pos,size] = self.tfactory.recalc(0)
            else:
                if self.app.sess_status.target_hits >= self.app.cfg.hit_tol:        # if target has been hit consecutively, decrease size
                    [pos,size] = self.tfactory.recalc(-1)
                    self.app.sess_status.target_hits = 0
                elif self.app.sess_status.target_misses >= self.app.cfg.miss_tol:   # if target has been missed consecutively, increase size
                    [pos,size] = self.tfactory.recalc(1)
                    self.app.sess_status.target_misses = 0
                else:
                    [pos,size] = self.tfactory.recalc(0)
                    
        self.set_stats(self.app.sess_status.target_hits, self.app.sess_status.target_misses, self.app.sess_status.result)
        
        # Make the box invisible now there is an image
        if self.app.cfg.use_image_as_target:
            self.rect.setPen( (0, 0, 0, 0) )
            self.rect.setBrush( (0, 0, 0, 0) )  
            
        else:
            self.img.setVisible(False)


       
        self.set_rectangle(pos = pos, size = size)
        self.app.sess_status.target_on = True
        

        # reset the axes
        self.wGraph.setXRange(0, self.xMax)
        self.wGraph.setYRange(0, self.app.cfg.y_axis_max)
        
        # set delay marker, get the time value of right-hand edge of target 
        self.app.sess_status.delay_mark = Decimal( str( float(self.tfactory.x2) + float(self.app.cfg.delay_len) ) )
        
    def __remove_target(self):
        """
        Remove old target

        """
        self.rect.setData([0],[0]) #Plots a point rather than a box
        
        if self.app.sess_status.target_on:
            #self.target.remove()
            self.app.sess_status.target_on = False
            
    def __draw_cursors(self , time, ampl):
        '''
        Draws the cursors if either the time or amplitude
        check boxes are given.
        
        ==============  =====================================
        **Arguments:**
        time            (int) Time at which to draw the
                         vertical cursor
        ampl            (int) amplitude at which to draw the
                         horizontal cursor
        ==============  =====================================
                
        '''
        checkBoxs = self.app.ctrl.chkbx_ctrl

        if checkBoxs.cb_time.IsChecked(): 
            yValues = [0,self.app.cfg.y_axis_max]
            xValues = [time,time]
            
            # Plot a vertical line at the current time
            self.timeLine.setData(xValues,yValues)
        else:
            self.timeLine.setData([0],[0])
            
            
        if checkBoxs.cb_ampl.IsChecked(): 
            yValues = [ampl,ampl]
            xValues = [0,self.xMax]
            # Plot a horizonatal line at the current amplitude
            self.ampLine.setData(xValues,yValues)
        else:
            self.ampLine.setData([0],[0])
        

    def end_trial(self):
        """
        Resets the plot between trials.This function should only be called after a trial run has been completed
        """
        self.app.sess_status.alive = False      # stop the plot
        gc.collect()
        
        ti = self.app.sess_status.trial_index
        mode = self.app.sess_status.mode
        
        # Record the result of the last trial
        self.app.sess_status.result = self.test_target()
         
        # ----------------------- Strength trial
        if mode == Modes.StrTrl:
            self.app.sess_status.type = "Strength"
            if self.app.sess_status.result:
                self.app.sess_status.target_hits += 1
                self.app.sess_status.target_misses = 0
                self.app.sess_status.count += self.app.cfg.hit_incr
            else:
                self.app.sess_status.target_misses += 1
                self.app.sess_status.target_hits = 0
                self.app.sess_status.count -= self.app.cfg.miss_incr
          

        # ----------------------- Skill trial
        elif mode == Modes.SklTrl:
            self.app.sess_status.type = "Skill"
            if self.app.sess_status.result:
                self.app.sess_status.target_hits += 1
                self.app.sess_status.target_misses = 0
                self.app.sess_status.count += self.app.cfg.hit_incr
            else:
                self.app.sess_status.target_misses += 1
                self.app.sess_status.target_hits = 0
                self.app.sess_status.count -= self.app.cfg.miss_incr

        tr = self.get_trial()       # create a database.Trial object
        self.app.sess_lock.acquire()    # ACQUIRE SESS_LOCK <--
        self.app.status.session.add_trial(tr)
        self.app.sess_lock.release()    # RELEASE SESS_LOCK -->

        if ti > 0:
            # Save trial outcome to history list
            htuple = (self.app.sess_status.peak, tr.target, self.app.sess_status.result)
            self.app.sess_status.history.append(htuple)
        
        # Print a short summary of the trial to the command line
        if mode == Modes.StrTrl:
            print ">> End   strength trial %i" % ti
        elif mode == Modes.SklTrl:
            print ">> End   skill trial %i" % ti
        if self.app.cfg.str_running_count:
            print '> Cumulative  count: %i' % (self.app.sess_status.count)
            print '> Tolerances   Hits: %i\tMisses: %i\n' % (self.app.cfg.str_hit_tol, self.app.cfg.str_miss_tol)
        else:
            print '> Consecutive  Hits: %i/%i\tMisses: %i/%i' % (self.app.sess_status.target_hits, 
                                                                 self.app.cfg.str_hit_tol, 
                                                                 self.app.sess_status.target_misses, 
                                                                 self.app.cfg.str_miss_tol)
        print tr.get_summary()      # Print a short summary of the last trial to the console

        if ti % self.app.cfg.trials_per_block == 0:  # end block
            gc.collect()
            self.app.sess_status.alive = False
            self.app.end_block()
            self.app.sess_status.block_index += 1
            # Wait until break is over (in worker thread)
            self.app.break_finished.wait()
            
        if ti >= self.app.cfg.trials_per_session:      # If all trials have been completed then end session
            gc.collect()
            self.app.end_session()
            self.dCntl.saveExitBtn.click()
            
        else:
            # Initialise plot for the next run
            self.app.sess_status.trial_index += 1
            self.init_plot()
            
    def init_plot(self):
        """ Initial plot setup at the start of calibration and trials
        """       
        self.reset_plot()
        
        alive = True       # begin adding jobs to the queue
        ci = self.app.sess_status.calib_index
        ti = self.app.sess_status.trial_index
        mode = self.app.sess_status.mode
        
        # ----------------------- Calibration modes
        if mode == Modes.StrCal:
            text = 'Strength Calibration: %i' % ci
            if ci == 0:
                alive = False
                self.app.ctrl.ymax_ctrl.set_auto(False)
                self.app.sess_status.ymax_auto = False

        elif mode == Modes.SklCal:
            text = 'Skill Calibration: %i' % ci
            if ci == 0:
                alive = False
                self.app.ctrl.ymax_ctrl.set_auto(False)
                self.app.sess_status.ymax_auto = False

        # ----------------------- Strength training mode
        elif mode == Modes.StrTrl:
            ymax_baseline = self.app.cfg.y_margin_strength
            text = ('Strength trial: %i' % ti)
            if ti == 0:     # if pre-trial
                alive = False
                self.app.ctrl.ymax_ctrl.set_value(self.app.sess_status.baseline * ymax_baseline)    # Scale the Y-axis range
                self.app.cfg.y_axis_max = int(self.app.ctrl.ymax_ctrl.get_value())
                
            else:
                print "-----------------------------------------------------------------"
                print ">> Begin strength trial %i" % ti
                self.__draw_target_strength()

        # ----------------------- Skill training mode
        elif mode == Modes.SklTrl:
            ymax_baseline = self.app.cfg.y_margin_skill
            text = ('Skill trial: %i' % ti)
            if ti == 0:     # if pre-trial
                alive = False     # disable plot update while waiting for beginning of trial 
                self.app.ctrl.ymax_ctrl.set_value(self.app.sess_status.baseline * ymax_baseline)    # Scale the Y-axis range
                #self.app.cfg.y_axis_max = int(self.app.ctrl.ymax_ctrl.get_value()) --removed 05/02
                self.tfactory.set_size_limits()
            else:
                print "-----------------------------------------------------------------"
                print ">> Begin skill trial %i" % ti
                self.__draw_target_skill()      # draw skill target
                
                if self.app.cfg.use_fading:    # adjust visibility of plot if fading is being implemented
                    self.set_fading()
                    
                elif self.app.cfg.use_delay:
                    self.set_delay()
                
                
        else:
            text = ''
            
        self.status_msg(text)      # Update trial text
        
        # Set the serial data acquisition thread to write values to arrays
        self.app.sess_status.alive = alive
        
             
    def status_msg(self,message):
        """
        Set the status message in the plot window
         ==============  =====================================
        **Arguments:**
        message         (char) Message to display
        ==============  =====================================

        """
        self.dSts.set_status(message)    
        
    def set_stats(self,hits,misses,prevRes,initial = False):
        """
        Sets the current consecutive hits and misses
         ==============  =====================================
        **Arguments:**
        hits            (int) number of consecutive hits
        misses          (int) number of consecutive misses
        prevRes         (bool) whether the previous result was successful 
        initial         (bool) whether this is the first trial
        ==============  =====================================

        """

        hitsDiag = "Consecutive Hits:" + str(hits)
        missDiag = "Consecutive Misses:" + str(misses)
                
        if initial: 
            prevDiag = "Previous Result: NA" 
        elif prevRes:
            prevDiag = "Previous Result: Hit" 
        else: 
            prevDiag = "Previous Result: Miss" 
        
        self.dSts.set_hits(hitsDiag) 
        self.dSts.set_miss(missDiag) 
        self.dSts.set_prev_result(prevDiag) 
        
    def set_default_display(self):
        '''Used to reset the plot'''
        self.dOpt.set_time(False)
        self.dOpt.set_amp(False)
        self.dOpt.set_grid(False)
        self.dOpt.set_plot(True)
        
    
    def set_delay(self):
        '''Used to implement delayed feedback'''
        
        # Turn off plot and update controls
        self.show_plot(False)
        # set time cursor on by default
        self.dOpt.set_time(True)      
        
    def set_fading(self):
        ''' used to implement fading'''
        
        block = self.app.sess_status.block_index
        sch = self.app.cfg.fading_schedule[block]
        on = self.app.sess_status.plots_on
        off = self.app.sess_status.plots_off
        phase = self.app.sess_status.fading_phase
        if phase:
            if off < sch[1]:
                self.__set_fading(True)
                self.dOpt.set_time(True)
                self.app.sess_status.plots_off += 1    # increment number of trials that have used fading (i.e. plot not visible)
            else:
                self.__set_fading(False)
                self.dOpt.set_time(False)
                self.app.sess_status.plots_on += 1
                self.app.sess_status.plots_off = 0          # reset counter
                self.app.sess_status.fading_phase = False   # change fading phase
        else:
            if on < sch[0]:
                self.__set_fading(False)
                self.dOpt.set_time(False)
                self.app.sess_status.plots_on += 1     # increment number of trials with plot visible
            else:
                self.__set_fading(True)
                self.dOpt.set_time(True)
                self.app.sess_status.plots_off += 1
                self.app.sess_status.plots_on = 0           # reset counter
                self.app.sess_status.fading_phase = True    # change fading phase
        
    def __set_fading(self, boo):
        # Used to implement fading schedule. Should be called at the start of each plot
        # init_plot() > set_fading() > __set_fading()
        boo = not boo       # invert value to set plot visible is equal to inverse of fading on
        self.show_plot(boo)
        
        
    def close(self):
        """
        Closes the plot

        """
        self.timer.stop()
        self.timer = None
        
        self.wGraph.close()
        self.mw.close()
        self.qapp.quit()
        
        
    def show_plot(self,answer):
        """
        Set the whether the sEMG plot displays
        
        ==============  =====================================
        **Arguments:**
        answer          (bool) Whether the plot should display
        ==============  =====================================
        """
        
        self.showCurve = answer
        
    def reset_plot(self):
        """
        Resets the data and time of the plot

        """
        self.x_data = np.array([])      # array of x values for the plot
        self.y_data = np.array([])      # array of y values for the plot
        self.emgdata = []
        self.plot_state= np.array([])  
        self.start_time = time.time()
        self.total_paused_time = 0
        self.time_pause = 0
        
        # Creates a white square to wipe the screen
        self.rect.setPen( (0, 0, 0, 0) )
        self.rect.setBrush( (0, 0, 0, 0) )
        self.set_rectangle(pos = [-1,-self.app.cfg.y_axis_max],size = [self.app.cfg.trial_len+1,self.app.cfg.y_axis_max*2])
        self.set_rectangle(size = [0,0]) # Resets Size
        
        self.rect.setPen(color =self.app.cfg.target_color ,width = self.app.cfg.target_line_width)
        self.rect.setBrush(self.app.cfg.target_color)
        
    def pause_time(self):
        """
        Pauses the real time clock for plotting

        """
        self.time_pause = time.time()
        
    def unpause_time(self):
        """
        Unpauses the real time clock for plotting

        """
        unpause_time = time.time()
        
        difference = unpause_time -  self.time_pause
        self.total_paused_time = self.total_paused_time + difference

        
    def set_rectangle(self,pos = [0,0],size = [1,1]):
        """
        Set the position and size of a rectangle
        
        ==============  =====================================
        **Arguments:**
        pos             (array) The x,y position of the bottom left
                        corner. [x,y] 
        size            (array) The x and y length of the rectangle
                        [xLength,yLength]
        ==============  =====================================
        """
        [x,y] = pos
        [xLen,yLen] = size
        
        x1 = x
        x2 = x + xLen
        y1 = y
        y2 = y + yLen
        
        #self.sizeRect.setCoords(x1,y1,x2,y2)
        #self.img.setRect(self.sizeRect)
        
        self.rect_x = [x1,x1,x2,x2]
        self.rect_y = [y1,y2,y2,y1]
        self.rect.setData(self.rect_x,self.rect_y)
        
        if self.app.cfg.use_image_as_target:
            [newPixWid,newPixHigh] = self.pos_to_pixel(xLen,yLen)
           
            
            # Set image to the size of the total window then rescale to desired size
            windowSize = self.area.geometry()
                    
            self.sizeRect= QtCore.QRect(0,0,windowSize.width(),windowSize.height())
            self.img.setRect(self.sizeRect)
            
            scaleX = newPixWid/windowSize.width() 
            scaleY = newPixHigh/windowSize.height() 
            
    
            self.img.scale(scaleX,scaleY)
            self.img.setPos(x1,y2)
        self.rect.setFillLevel(y1) # Fills from the top of the curve to this level
        
    def show_rectangle(self,answer):
        """
        Can show or hide rectangle
        
        ==============  =====================================
        **Arguments:**
        answer          (bool) Whether to show the rectangle
        ==============  =====================================
        """
        
        if answer:
            self.rect.setData(self.rect_x,self.rect_y)
            self.rect.setFillLevel(self.rect_y[0]) # Fills from the top of the curve to this level
        else:
            self.rect.setData([0,0],[0,0])
            self.rect.setFillLevel(0)
            
    def test_target(self):
        """
        Test the target line or box and return a boolean value indicating a hit or miss
    
        """
        # Test the target line or box and return a boolean value indicating a hit or miss
        result = False
        
        if self.app.sess_status.mode == Modes.StrTrl:          # STRENGTH training
            if self.app.sess_status.peak[1] > self.app.sess_status.str_target:

                result = True

        elif self.app.sess_status.mode == Modes.SklTrl and (self.x_data.any()):        # SKILL training
            # Test to see if the last peak falls inside the target
            # Start by creating an array containing just the values around the target
            x1 = self.tfactory.x1 - self.app.cfg.target_test_margin
            x2 = self.tfactory.x2 + self.app.cfg.target_test_margin
            if x1 < 0: x1 = 0
            if x2 > self.app.cfg.trial_len: x2 = self.app.cfg.trial_len
            
            xLowPos = next(x[0] for x in enumerate(self.x_data) if x[1] > x1) # find the position where x is 
            xHighPos = next(x[0] for x in enumerate(self.x_data) if x[1] > x2)
            
            ta = self.y_data[xLowPos:xHighPos]
            try:
                xPos = ta.argmax() + xLowPos        # get the index of the max value inside margins
                y = self.y_data[xPos]          # record the actual value
                x = self.x_data[xPos]
                self.app.sess_status.peak = (x, y)
                if self.tfactory.x1 < x and x < self.tfactory.x2 and self.tfactory.y1 < y and y < self.tfactory.y2:
                    result = True
            except:
                print "x1 =", x1
                print "x2 =", x2
                print "tested array =", ta
        return result
            
    def reset_target(self):
        """
        Reset the target to baseline values

        """

        if self.app.sess_status.mode == Modes.StrTrl:
            self.__draw_target_strength(init=True)
        elif self.app.sess_status.mode == Modes.SklTrl:
            self.__draw_target_skill(init=True)
            
            
    def get_trial(self):
        '''
        Returns a database.Trial object constructed from the current plot data
        '''
        # check that the array are the same length and truncate the longer if not
        lenx = len(self.x_data)
        leny = len(self.y_data)
        if not lenx == leny:
            diff = leny - lenx
            if diff > 0:                # if y_data has more items than x_data...
                self.y_data = self.y_data[:-diff]   # remove items from y_data to even up the arrays
            elif diff < 0:              # if x_data has more items than y_data...
                self.x_data = self.x_data[:-diff]   # remove items from x_data to even up the arrays
            
        # Record the raw emgdata
        for i in range(len(self.y_data)):
            self.emgdata.append([self.x_data[i], self.y_data[i], self.plot_state[i]])
            
        # Calculate target positions
        pos = [self.rect_x[0],self.rect_y[0]]
        size = [self.rect_x[2] - self.rect_x[0],self.rect_y[1] - self.rect_y[0] ]
        
        # Create a trial object for this trial and append to the current session object
        tr = database.Trial(len=self.app.cfg.trial_len,
                            typ=self.app.sess_status.type,
                            peak=self.app.sess_status.peak,
                            result=self.app.sess_status.result,
                            emgdata=self.emgdata,
                            target_pos = pos,
                            target_size = size,
                            aspect=self.app.cfg.target_aspect_ratio)
        return tr

# End of NewPlot.py
        
